#include "Thread.hpp"
#include "LockGuard.hpp"
#include "Log.hpp"

#include <vector>
#include <queue>
#include <functional>

const int default_thread_num = 5;

template <class T>
class ThreadPool
{
private:
    void HandleTask(std::string name)
    {
        LOG(INFO, "%s is running", name.c_str());

        while (true)
        {
            pthread_mutex_lock(&_mtx);

            // 队列为空 且 正在运行
            while (_q.empty() && _IsRunning)
            {
                _waitnum++;
                pthread_cond_wait(&_cond, &_mtx);
                _waitnum--;
            }

            // 队列为空 且 不再运行
            if (!_IsRunning && _q.empty())
            {
                pthread_mutex_unlock(&_mtx);
                break;
            }

            // get task
            T t = _q.front();
            _q.pop();
            pthread_mutex_unlock(&_mtx);

            LOG(DEBUG, "%s get a task", name.c_str());

            // handle task
            t();
        }
    }

    ThreadPool(int threadNum = default_thread_num)
        : _thread_num(threadNum), _waitnum(0), _IsRunning(false)
    {
        pthread_cond_init(&_cond, nullptr);
        pthread_mutex_init(&_mtx, nullptr);
    }

    ThreadPool(ThreadPool&) = delete;
    ThreadPool& operator=(ThreadPool&) = delete;
public:

    static ThreadPool<T>* GetInstance()
    {
        if(_instance == nullptr)
        {
            LockGuard Lock(&_lock);
            if(nullptr == _instance)
            {
                _instance = new ThreadPool<T>();
                LOG(INFO, "创建单例");
                
                return _instance;
            }
        }
        LOG(INFO, "获取单例");
        return _instance;
    }

    void Init()
    {
        for(int i = 0; i < _thread_num; ++i)
        {
            std::string name = "Thread-";
            name += std::to_string(i + 1);
            _threads.emplace_back(Thread(name, std::bind(&ThreadPool::HandleTask, this, name)));
            LOG(INFO, "init %s done", name.c_str());
        }
    }

    void Start()
    {
        _IsRunning = true;
        for(int i = 0; i < _thread_num; ++i)
        {
            _threads[i].Start();
            LOG(INFO, "%s start success", _threads[i].GetName().c_str());
        }
        
        
    }

    void Stop()
    {
        pthread_mutex_lock(&_mtx);

        _IsRunning = false;
        pthread_cond_broadcast(&_cond);

        pthread_mutex_unlock(&_mtx);

        LOG(DEBUG, "all thread stop...");
    }

    void Join()
    {
        for(int i = 0; i < _thread_num; ++i)
        {
            LOG(INFO, "%s is join...", _threads[i].GetName().c_str());
            _threads[i].Join();
            
        }
    }

    bool EnQueue(T t)
    {
        LOG(DEBUG, "add a Task");
        bool ret = false;
        pthread_mutex_lock(&_mtx);

        if(_IsRunning)
        {
            _q.push(t);
            if(_waitnum > 0)
            {
                pthread_cond_signal(&_cond);
            }
            ret = true;
        }

        pthread_mutex_unlock(&_mtx);

        return ret;
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }

private:
    int _thread_num;
    std::vector<Thread> _threads;
    std::queue<T> _q;
    int _waitnum;
    bool _IsRunning;

    pthread_mutex_t _mtx;
    pthread_cond_t _cond;

    static pthread_mutex_t _lock;
    static ThreadPool<T>* _instance;
};

template<class T>
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;

template<class T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;